<?php
/**
 * visitor.class
 *
 * Класс для хранения данных о посетителе
 * Visitor class for keeping data of current visitor
 * 
 * @version:	0.4
 * @package:	flame.core
 * @author:		alex_ez
 * @created:	11.11.2008 16:17:50
 * 
 * @copyright:	(c) 2008 flamebox.ru. All rights reserved.
 */

if( defined( '__VISITOR.CLASS_PHP' ) ) return;
define( '__VISITOR.CLASS_PHP', '0.4' );

class visitor extends singleton
{
	// php singleton wrapper
	static public function &__getMe( ) { return parent::__getMe(__CLASS__); }
	static public function &instance() { return parent::__getMe(__CLASS__); }
	
	private $_started = null;
	
	private $name = null, $mail = null, $time = null, $life = null;
	private $auth_controller_name = null;
	private $flash_messages = null;
	private $flash_messages_keep = array( );
	
	/**
	 * Конструктор
	 *
	 * @access protected
	 */
	protected function __construct( )
	{
		if( defined( 'VISITOR_INSTANCE' ) ) { exec( 'echo '.escapeshellarg('second visitor instance').' >> '.escapeshellarg(root.'.sesslog') ); return null; }
		define( 'VISITOR_INSTANCE', 1 );
		
		// Запускаем сессию, если она еще не запущена
		$r = request::__getMe();
		// for flash-queries sid allowed in GET only. flush cookie
		if( $r ->isFlash() )
		{
			ini_set( 'session.use_cookies', 0 );
			ini_set( 'session.use_only_cookies', 0 );
		}
		
		/**
		 * Всяка шняга
		 */
		if( isset( $r ->c_visitor ) )
		try {
			$_data = @unserialize( base64_decode( str_rot13( $r->c_visitor ) ) );
			fb::log( $_data, 'cookies' );
			$this ->time = isset( $_data['time'] ) ? $_data['time'] : time( );
			$this ->name = isset( $_data['name'] ) ? $_data['name'] : null;
			$this ->mail = isset( $_data['mail'] ) ? $_data['mail'] : null;
			$this ->life = isset( $_data['life'] ) ? (int)($_data['life']) : null;
		} catch( Exception $e ) {
			$this ->time = time( );
			$this ->name = 'johndoe';
			$this ->mail = null;
		}
		$sessionLifeTime = ini_get( 'session.cookie_lifetime' );
		if( !is_null( $this ->life ) )
			ini_set( 'session.cookie_lifetime', $this ->life );
		//setcookie( 'dlife', serialize($sessionLifeTime), 0 , '/', ini_get('session.cookie_domain') );
		//setcookie( 'life', serialize($this ->life), 0 , '/', ini_get('session.cookie_domain') );
		
		if( $r ->isFlash() && $r->c_sid ) { setcookie( 'sid', '', time()-360000, '/', ini_get('session.cookie_domain') ); }
		if( $r ->isFlash() && !$r->g_sid ) response::__getMe()->teapot();
		$sid = $r ->isFlash() ? $r ->g_sid : $r ->c_sid;
		
		// set session_id
		if( isset($sid)&&!is_null($sid)&&strlen($sid)>10 ) session_id( $sid );
		// restart old session (or generate new one)
		@session_start( );
		$sid = session_id( );
		//exec( 'echo '.escapeshellarg($r->getIp().' '.$sid).' >> '.escapeshellarg(root.'.sesslog') );
		
		fb::group( 'visitor', array( 'Collapsed' => true, 'Color' => '#777' ) );
		fb::log( $sid, 'sid' );
		if( $this ->signed )
		{
			fb::log( $this ->signed, 'id' );
			fb::log( $this ->user['login'], 'login' );
			fb::log( $this ->_roles, 'roles' );
		}
		else
		{
			$this ->life = null;
			ini_set( 'session.cookie_lifetime', $sessionLifeTime );
		}
		$s = $_SESSION; unset( $s['user'] ); unset( $s['_roles'] ); unset( $s['signed'] ); fb::log( $s, 'session' ); unset( $s );
		
		// Устанавливаем локаль — убрано в конфиг
		//setlocale( LC_ALL, 'ru_RU.UTF-8' );
		//setlocale( LC_NUMERIC, 'C' );
		mb_internal_encoding( 'utf8' );
		
		fb::groupEnd( );

		$flash = $this ->flash;
		if( isset( $this ->flash ) && !empty( $flash ) && is_array( $flash ) )
			$this ->flash_messages = $flash;
		
		$this ->_started = true;
	}
	
	public function __destruct( )
	{
		if( !$this ->_started ) return;
		
		$data = array( );
		$data['time'] = $this ->time ? $this ->time : time( );
		if( $this ->name ) $data['name'] = $this ->name;
		if( $this ->mail ) $data['mail'] = $this ->mail;
		if( !is_null( $this ->life ) ) $data['life'] = $this ->life;

		setcookie( 'visitor', str_rot13( base64_encode( serialize( $data ) ) ), strtotime( "next year" ), '/', ini_get('session.cookie_domain') );

		unset( $this ->flash );
		if( !empty( $this ->flash_messages ) && is_array( $this ->flash_messages ) && !empty( $this ->flash_messages_keep ) )
		{
			$keep = array( );
			foreach( $this ->flash_messages_keep as $key )
				if( isset( $this ->flash_messages[ $key ] ) )
				$keep[$key] = $this ->flash_messages[$key];
			$this ->flash = $keep;
		}
		
		$this ->_started = false;
	}
	
	
	public function signin( $user, $time = null )
	{
		$this ->signed = $user['id'];
		$this ->user = $user;
		$this ->_roles = explode( ',', $user['roles'] );
		
		if( !is_null( $time ) )
		{
			ini_set( 'session.cookie_lifetime', $time );
			$this ->life = $time;
		}
	}
	
	public function signout( )
	{
		$this ->signed = null;
		$this ->user = null;
		$this ->_roles = null;
		$this ->life = null;
	}
	
	public function refresh( )
	{
		if( !$this ->isSigned( ) ) return null;
		return $this ->getAuthController( ) ->resign( );
	}
	
	public function isSigned( )
	{
		return !is_null( $this ->getUserId() );
	}
	
	public function needAuth( $state = null, $strict = null )
	{
		// shout ajax, flash queries and ugly queries from web
		$request = request::__getMe( );
		if( $request ->isBot()
		 || !$this ->isSigned() && ( $request ->isAjax() || $request ->isFlash() ) )
			response::__getMe() ->forbidden( ); //403
		
		$config = config::__getMe( );
		
		// redirects
		$url = $request ->getUrl( );
		$path = $this ->getAuthPath( );
		
		if( $url != $path.'signin/' && $url != $path && $url != '/' )
			$this ->realReferer = $url;
		
		//var_dump( request::__getMe() ->getReferer( ), visitor::__getMe( ) ->referer, request::__getMe() ->getUrl( ) ); die;
		if( !$this ->isSigned( ) )
			response::__getMe() ->seeother( $path.'signin/' );
		
		// cut! or not?..
		$strict = is_null($strict) ? ( $config ->roles == 'strict' ) : $strict;
		$state = is_null($state) ? 'person' : $state; // default state is person.
		
		if( $strict && $this ->user['state'] != $state )
		{
			$mustBeAnUrl = str_replace( '/person/', '/', '/'.$this ->user['state'].'/' );
			if( $url != $mustBeAnUrl )
			//fb::log( array( 'role' => $state, 'state' => $this ->user['state'] ) );
				response::__getMe() ->seeother( $mustBeAnUrl );
		}
	}
	
	/**
	 * move to auth controller
	 */
	public function getAuthPath( )
	{
		return '/'.$this ->getAuthControllerName( ).'/';
	}
	
	public function getAuthController( )
	{
		return controller::__getInstanceOf( $this ->getAuthControllerName( ) );
	}
	
	public function getAuthControllerName( )
	{
		if( is_null( $this ->auth_controller_name ) )
			$this ->auth_controller_name = trim(_::pick(config::__getMe( ) ->auth__controller,'auth'),"\r\n\t /\\");
		
		return $this ->auth_controller_name;
	}
	
	
	
	
	
	public function __set( $k, $v = null )
	{
		if( !is_null( $v ) ) $_SESSION[ $k ] = $v; else unset( $_SESSION[ $k ] );
	}
	
	public function __get( $k )
	{
		switch( $k )
		{
			case 'name': 
			case 'mail': 
			case 'time': return $this ->$k; break;
			case 'roles':
				return new Roles( $this ->_roles );
			case 'viewuser':
				$_user = $this ->user;
				unset( $_user[ 'password' ] );
				return new ViewUser( $_user );
			default:
			return isset( $_SESSION[ $k ] ) ? $_SESSION[ $k ] : null;
		}
	}
	
	public function __isset( $k )
	{
		return isset( $_SESSION[$k] );
	}

	public function __unset( $k )
	{
		unset( $_SESSION[ $k ] );
	}
	
	/**
	 * some methods
	 * 
	 */
	public function getUserId( )
	{
		return $this ->signed;
	}
	
	public function getUserLogin( )
	{
		return $this ->user['login'];
	}
	
	public function getUserInfo( )
	{
		$us = new users( );
		$r = $us ->get( $this ->signed );
		$user = array( );
		foreach( explode( ',', 'id,name,state' ) as $k )
			$user[ $k ] = $r[ $k ];
		
		return $user;
	}
	
	public function sid( $clear = false )
	{
		return $clear ? session_id() : sprintf( "%s=%s", session_name(), urlencode( session_id() ) );
	}


	// flash data
	public function flash( $key = null, $message = null )
	{
		switch( is_null( $key ) ? 0 : ( is_null( $message ) ? 1 : 2 ) )
		{
			case 0:
				return (array)($this ->flash_messages);
			case 1:
				if( isset( $this ->flash_messages[$key] ) )
					return $this ->flash_messages[$key];
				return null;
				break;
			case 2:
				$this ->keep_flash( $key );
				$this ->flash_messages[$key] = $message;
		}
		return $this;
	}

	public function keep_flash( $key )
	{
		$this ->flash_messages_keep[] = $key;
		return $this;
	}
}

?>